<?php

namespace app\modules\api\controllers;

use yii;
use ac\controllers\BasicController;
use app\models\Member;
use app\models\DealLog;
use ac\component\tool\Tool;
use ac\component\exception\ErrMap;
use ac\component\tool\HttpClient;
use app\models\OperationLog;

use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
use WeChatPay\Formatter;
use WeChatPay\Crypto\AesGcm;



class WechatController extends BasicController
{
    private $appid = "wx4a06a78701ebdf73";
    private $appSecret = "da0671b59d4a31cc61823fd612c1efff";
    private $merchantId = "1617577221";
    private $key = "1617577221";
    private $merchantPrivateKeyFilePath = "file:///www/cert/paijian8/wechatpay/apiclient_key.pem";
    private $platformCertificateFilePath = "file:///www/cert/paijian8/wechatpay/cert.pem";

    /*授权登录*/
    public function actionLogin()
    {
        // P($this->post);
        $code = isset($this->post['code'])?$this->post['code']:'';
        $appId = $this->appid;
        $appSecret = $this->appSecret;
        if (!empty($code)){
            $url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $appId . "&secret=" . $appSecret . "&js_code=" . $code . "&grant_type=authorization_code";
            $httpClient = new HttpClient($url);
            if($res = $httpClient->post() and $res['errcode'] == 0){
                // P($res);
                $openid = $res['openid'];
                /*查看库中是否存在*/
                $wechat = Member::find()
                      ->where('wechat_openid = :openid', [':openid' => $openid])
                      ->one();
                // P($wechat);
                if(is_null($wechat)){
                    /*入库*/
                    $this->post['wechat_openid'] = $openid;
                    $this->post['regip'] = Yii::$app->request->userIP;
                    $this->post['regclient'] = $_SERVER['HTTP_USER_AGENT'];
                    $this->post['regfrom'] = 3;//来源移动
                    $data['Member'] = $this->post;
                    // P($data);
                    $member = new Member;
                    $res = $member->addMember($data);
                    
                    if($res and $res->code == 0){
                        $info = Member::find()
                            ->select(['member_id', 'loginname', 'phone', 'email', 'nickname', 'sex', 'signature', 'headimgurl', 'wechat_openid', 'add_time'])
                            ->where('wechat_openid = :wechatid', [':wechatid' => $openid])
                            ->asArray()->one();
                        if(!is_null($info)){
                            Yii::$app->session->set('loginInfo', (object)$info);
                            $returnRes = array(
                                'openid' => $openid,
                                'sessionid' => session_id(),
                            );

                            return Tool::showRes([0, '授权成功'], $returnRes);
                        }
                    }else{
                        return Tool::showRes(ErrMap::getError(10025));
                    }
                }else{
                    /*增加最后登录时间、最后登录IP、登录总数*/
                    Member::updateAll(['lastloginip' => Yii::$app->request->userIP, 'lastlogin_time' => date('Y-m-d H:i:s', time())], 'wechat_openid = :wechatid', [':wechatid' => $openid]);
                    Member::updateAllCounters(['logintimes' => 1], 'wechat_openid = :wechatid', [':wechatid' => $openid]);
                    $backInfo = [0, '授权成功'];//返回信息
                    $info = Member::find()
                            ->select(['member_id', 'loginname', 'phone', 'email', 'nickname', 'sex', 'signature', 'headimgurl', 'wechat_openid', 'add_time'])
                            ->where('wechat_openid = :wechatid', [':wechatid' => $openid])
                            ->asArray()->one();
                    if(!is_null($info)){
                        Yii::$app->session->set('loginInfo', (object)$info);
                        $returnRes = array(
                            'openid' => $openid,
                            'sessionid' => session_id(),
                        );
                        return Tool::showRes([0, '授权成功'], $returnRes);
                    }else{
                        return Tool::showRes(ErrMap::getError(10025));
                    }
                }
            }else{
                return Tool::showRes(ErrMap::getError(10025, $res['errmsg']));
            }
        }
        return Tool::showRes(ErrMap::getError(10005));
    }

    /*退出登录*/
    public function actionLogout()
    {
        Yii::$app->session->removeAll();
        if(Yii::$app->session->get('loginInfo') === null){
            return Tool::showRes([0, '成功登出']);
        }else{
            return Tool::showRes(ErrMap::getError(10006));
        }
    }


    /*支付*/
    public function actionPay()
    {
        if(Yii::$app->session->get('loginInfo') != null){
            $this->userInfo = Yii::$app->session->get('loginInfo');
        };
        // P($this->userInfo);
        /*验证登录*/
        if(empty($this->userInfo)){
            return Tool::showRes(ErrMap::getError(10007));
        }

        if(Yii::$app->request->isPost){
            // P($this->post);
            $this->post['DealLog']['member_id'] = $this->userInfo->member_id;
            if(isset($this->post['order_id'])) {
                $this->post['DealLog']['order_id'] = (int)$this->post['order_id'];
            }
            $money = (int)(isset($this->post['money'])?$this->post['money']:0);
            $this->post['DealLog']['orderid'] = date("YmdHis") . Tool::generateRandomStr(6, 'num');
            $this->post['DealLog']['money'] = $money * 100;
            $this->post['DealLog']['flag'] = isset($this->post['flag'])?$this->post['flag']:3;
            $this->post['DealLog']['way'] = isset($this->post['way'])?$this->post['way']:'';
            // P($this->post);
            $dealLog = new DealLog;
            if($dealLog->addLog($this->post)){
                $backInfo = [0, '添加交易记录成功'];//返回信息
            }else{
                if($dealLog->hasErrors()){
                    $backInfo = ErrMap::getError(10005);//返回信息
                    $exception = $dealLog->getErrors();//异常信息
                }
            }
        };

        if(!isset($backInfo)){
            $backInfo = ErrMap::getError(10005);//返回信息
        }
        if(!isset($exception)){
            $exception = '';//异常信息
        }
        //操作日志
        if(gettype( $res = OperationLog::addLog(Tool::operationLogArgs($backInfo, $this->userInfo), $exception) ) != 'boolean'){
            // return $res;
            // P($this->userInfo);
            $params = $this->getJsapi($money, $this->post['DealLog']['orderid'], $this->userInfo->wechat_openid);
            // P($params);
            if($params !== false){
                return Tool::showRes([0, '下单成功'], $params);
            }
        };
    }


    /*支付JSAPI*/
    private function getJsapi($money, $out_trade_no, $openid)
    {
        /*验证登录*/
        // if(empty($this->userInfo)){
        //     return Tool::showRes(ErrMap::getError(10007));
        // }
        $money = (int)$money;
        if(!$money){
            return false;
        }

        $money = $money * 100;

        $appid = $this->appid;//appid
        $merchantId = $this->merchantId;//商户号
        $merchantCertificateSerial = "64D8D793879C26A0121D7FC79B2EEB0FF9BF03D0";//商户API证书 的 证书序列号
        // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
        $merchantPrivateKeyFilePath = $this->merchantPrivateKeyFilePath;
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
        // 从本地文件中加载「微信支付平台证书」，用来验证微信支付应答的签名
        $platformCertificateFilePath = $this->platformCertificateFilePath;
        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

        // 从「微信支付平台证书」中获取「证书序列号」
        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

        // 构造一个 APIv3 客户端实例
        $instance = Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs' => [
                $platformCertificateSerial => $platformPublicKeyInstance,
            ],
        ]);

        $out_trade_no = $out_trade_no;
        $data = [
            'appid' => $appid,//应用ID
            'mchid' => $merchantId,//直连商户号
            'description' => "鉴品费用",//商品描述
            'out_trade_no' => $out_trade_no,//商户订单号
            'notify_url' => "https://paijian8.uthinks.cn/api/wechat/notify",//通知地址
            'amount' => [//订单金额
                'total' => $money,//总金额
                'currency' => "CNY",//货币类型
            ],
            'payer' => [//支付者
                'openid' => $openid,//用户标识
            ]
        ];
        // return $data;
        try {
            $resp = $instance
            ->chain('v3/pay/transactions/jsapi')
            ->post(['json' => $data]);

            // echo $resp->getStatusCode(), PHP_EOL;
            // echo $resp->getBody(), PHP_EOL;

            $res = json_decode($resp->getBody());
            // echo $res->prepay_id;

            /*计算签名*/
            $params = [
                'appId'     => $this->appid,
                'timeStamp' => (string)Formatter::timestamp(),
                'nonceStr'  => Formatter::nonce(),
                'package'   => 'prepay_id=' . $res->prepay_id,
            ];
            $params += ['paySign' => Rsa::sign(
                Formatter::joinedByLineFeed(...array_values($params)),
                $merchantPrivateKeyInstance
            ), 'signType' => 'RSA'];

            // echo json_encode($params);
            // die;

            return $params;
            // return Tool::showRes([0, '下单成功'], $params);
        } catch (\Exception $e) {
            return false;
            // 进行错误处理
            echo $e->getMessage(), PHP_EOL;
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
                echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
            }
            echo $e->getTraceAsString(), PHP_EOL;
        }
    }


    /*回调地址*/
    public function actionNotify() {
        // P($_SERVER);
        $inWechatpaySignature = $_SERVER['HTTP_WECHATPAY_SIGNATURE'];// 请根据实际情况获取
        $inWechatpayTimestamp = $_SERVER['HTTP_WECHATPAY_TIMESTAMP'];// 请根据实际情况获取
        $inWechatpaySerial = $_SERVER['HTTP_WECHATPAY_SERIAL'];// 请根据实际情况获取
        $inWechatpayNonce = $_SERVER['HTTP_WECHATPAY_NONCE'];// 请根据实际情况获取
        $inBody = file_get_contents('php://input');// 请根据实际情况获取，例如: file_get_contents('php://input');

        $apiv3Key = 'v6H4pmSTZpSn4EFPvgw6yxZQBzif4VX9';// 在商户平台上设置的APIv3密钥

        // 根据通知的平台证书序列号，查询本地平台证书文件，
        // 假定为 `/path/to/wechatpay/inWechatpaySerial.pem`
        $platformPublicKeyInstance = Rsa::from($this->platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

        // 检查通知时间偏移量，允许5分钟之内的偏移
        $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
        $verifiedStatus = Rsa::verify(
            // 构造验签名串
            Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
            $inWechatpaySignature,
            $platformPublicKeyInstance
        );
        if ($timeOffsetStatus && $verifiedStatus) {
            // 转换通知的JSON文本消息为PHP Array数组
            $inBodyArray = (array)json_decode($inBody, true);
            // 使用PHP7的数据解构语法，从Array中解构并赋值变量
            ['resource' => [
                'ciphertext'      => $ciphertext,
                'nonce'           => $nonce,
                'associated_data' => $aad
            ]] = $inBodyArray;
            // 加密文本消息解密
            $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
            // 把解密后的文本转换为PHP Array数组
            $inBodyResourceArray = (array)json_decode($inBodyResource, true);
            // print_r($inBodyResourceArray);// 打印解密后的结果

            /*改变支付状态*/
            $dealLog = new DealLog;
            $dealLog = $dealLog->find()
                            ->where('orderid = :id', [':id' => $inBodyResourceArray['out_trade_no']])
                            ->one();
            // $this->test(json_encode($dealLog['member_id']));
            if(!is_null($dealLog)){
                /*查询当前余额， 为了记录余额*/
                // $this->test($dealLog['member_id']);
                $member = new Member();
                $uinfo = $member->find()->select('account')
                                ->where('member_id = :id', [':id' => $dealLog->member_id])
                                ->one();
                // $this->test(json_encode($uinfo));
                if(!is_null($uinfo)){                
                    $dealLog->callback_header = json_encode($_SERVER);
                    $dealLog->callback_encrypt_data = $inBody;
                    $dealLog->callback_decrypt_data = $inBodyResource;
                    $dealLog->callback_time = date('Y-m-d H:i:s', time());
                    $dealLog->pay_status = 2;
                    if($dealLog->way == 2) {//充值才需要修改余额
                        $dealLog->balance = $uinfo->account + $inBodyResourceArray['amount']['total'];
                    } else {
                        $dealLog->balance = $uinfo->account;
                    }
                    if($dealLog->save(false) and $dealLog->way == 2){
                        Member::updateAll(['account' => $dealLog->balance], 'member_id = :id', [':id' => $dealLog->member_id]);
                        // $this->test("ok");
                    }else{
                        // $this->test("on");
                    }
                }
            }
            // if($dealLog->modifyStatus($data, $inBodyResourceArray['out_trade_no'])){
            //     $backInfo = [0, '修改信息成功'];//返回信息
            // }else{
            //     if($dealLog->hasErrors()){
            //         $backInfo = ErrMap::getError(10005);//返回信息
            //         $exception = $admin->getErrors();//异常信息
            //     }
            // }
            // $this->test($inBodyResourceArray['out_trade_no']);
        }
    }

    public function test($v) {
        // $v = file_get_contents("php://input");//回调回来的数据
        define('BASE_PATH', $_SERVER['DOCUMENT_ROOT']);//项目的路径
        $dir = BASE_PATH.'/backdata/'.date("Ymd")."/";//写入的路径
        // P($dir);
        if(!file_exists($dir)){//没有文件夹创建
            mkdir($dir, 0777, true);
        }
        $fileName = 'backdata.txt';
        // $fileName = rand(0, 1000) . '.txt';
        file_put_contents($dir . $fileName, $v, FILE_APPEND);//数据写入文件
    }

}
